/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sk.ke.szatmary.peter.jclac.simpleCiphers;

import sk.ke.szatmary.peter.jclac.simpleCiphers.core.SimpleCipherException;
import sk.ke.szatmary.peter.jclac.simpleCiphers.core.SimpleCipher;
import sk.ke.szatmary.peter.jclac.simpleCiphers.core.AbstractCipher;
import java.util.ArrayList;
import java.util.List;

/**
 * Caesar, je to transpozicna sifra, kde sa kazde pismeno posunie a urcity
 * pocet miest urcitym smerom, desifrovanie ide smerom opacnym, pouziva caesar o 3
 * 
 * Usage:<br /><br />
 * 
 * 
 * first attribute (0): Integer keyMove<br />
 * + Integer to right<br />
 * - Integer to left<br />
 * 
 * Integer: movement of char about number in ascii table = move<br />
 * , - Integer to left: direction of movement of char in ascii table <br /><br />
 * 
 * with chars 0 (START_UNICODE) - 65533(END_UNICODE) in ASCII

 * crypting examples:<br /><br />
 * 
 * 
 * @author nue
 */
public final class Caesar extends AbstractCipher implements SimpleCipher {

    /**
     * constructor without parameters,
     * default atribute is Integer with value 3
     */
    public Caesar() {
        attrs = new ArrayList<SimpleCipherAttr>();
        attrs.add(new SimpleCipherAttr(new Integer(3)));
        transformAttribute();
    }

    /**
     * one parametric constructor
     * @param attrs with atributes of cipher
     * @throws SimpleCipherException throw if is not fulfilled
     * <ul>
     * <li>one attribute</li>
     * <li>atribute with Integer type</li>
     * <li>attribute in range -65533 to 65333</li>
     * </ul>
     */
    public Caesar(List<SimpleCipherAttr> attrs) throws SimpleCipherException {
        this.attrs = attrs;
        this.checkAtributesForInit(this.attrs);
        transformAttribute();
    }

    /**
     * if move attribute is negativ than transform to positive value 
     */
    private void transformAttribute() {
        Integer move = (Integer)this.attrs.get(0).getValue();
        if (move < 0) {
            // zrob kladnu hodnotu ak treba
            move = move + END_UNICODE;
        }
        this.attrs.get(0).setChangedValue(move);
    }

    /**
     * check initialization attributes on list
     * @param attrs attributes of cipher
     * @throws SimpleCipherException see one parametric constructor
     */
    private void checkAtributesForInit(List<SimpleCipherAttr> attrs) throws SimpleCipherException {
        // must be one atributes
        if (attrs.size() != 1) {
            throw new SimpleCipherException("Atributes in List<SimpleCipherAttr> must be 1, size of list must be exactly 1, found size " + attrs.size());
        }

        // check type
        for (int i = 0; i < attrs.size(); i++) {
            Object value = attrs.get(i).getValue();
            if (!((value instanceof Integer))) {
                throw new SimpleCipherException("One of arguments in List<SimpleCipherAttr> must be Integer");
            }
        }

        // range of Integer value
        Integer move = (Integer) this.attrs.get(0).getValue();
        if (!(move > END_MINUS_UNICODE && move <= END_UNICODE)) {
            throw new SimpleCipherException("Integer value must be in range " + (END_MINUS_UNICODE + 1) + "-" + END_UNICODE);
        }

    }

    @Override
    public String toString() {
        return this.getName() + "{" + "original move=" + (Integer) this.attrs.get(0).getValue() + "transformated move=" + (Integer) this.attrs.get(0).getChangedValue()+ "}";
    }

    @Override
    public String getEncrypted(String plain) {

        Integer move = (Integer) this.attrs.get(0).getChangedValue();
        
        char[] encrypt = new char[plain.length()];

        for (int i = 0; i < encrypt.length; i++) {
            encrypt[i] = (char) ((plain.charAt(i) + move) % END_UNICODE);

        }
        return new String(encrypt);
    }

    @Override
    public String getDecrypted(String encrypted) {

        Integer move = (Integer) this.attrs.get(0).getChangedValue();
        
        char[] decrypt = new char[encrypted.length()];

        for (int i = 0; i < decrypt.length; i++) {
            int check = encrypted.charAt(i) - move;
            if (check >= 0) {
                decrypt[i] = (char) ((encrypted.charAt(i) - move) % END_UNICODE);
           
            } else {
                decrypt[i] = (char) ((encrypted.charAt(i) - move + END_UNICODE) % END_UNICODE);
                
            }
        }
        return new String(decrypt);
    }

    @Override
    public List<SimpleCipherAttr> getAttributes() {
        return super.getAtrributes();
    }

    @Override
    public String getCipherName() {
        return super.getName();
    }

    @Override
    public final void setAttributes(List<SimpleCipherAttr> list) throws SimpleCipherException {
        this.checkAtributesForInit(list);
        super.setAtrributeList(list);
        transformAttribute();
    }
}
